home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 23 code / Internet Config / IC 1.1 / ICProgKit1.1 / Source / Internet Config Source / ICRAPI.p < prev    next >
Encoding:
Text File  |  1995-04-23  |  39.9 KB  |  1,333 lines  |  [TEXT/PJMM]

  1. unit ICRAPI;
  2.  
  3. interface
  4.  
  5.     uses
  6. {$ifc undefined THINK_Pascal}
  7.         Types, Files, QuickDraw, Aliases, 
  8. {$endc}
  9.         Components, ICTypes, ICKeys;
  10.  
  11.     type
  12.         ICRRecord = record                    (* this is *completely* private to the implementation!!! *)
  13.                 instance: ComponentInstance;        (* nil if no component available, if not nil then rest of record is junk *)
  14.                 have_config_file: boolean;
  15.                 config_file: FSSpec;
  16.                 config_refnum: integer;
  17.                 perm: ICPerm;
  18.                 inside_begin: boolean;
  19.                 default_filename: Str63;
  20.             end;
  21.         ICRRecordPtr = ^ICRRecord;
  22.  
  23.     function ICRStart (var inst: ICRRecord; creator: OSType): ICError;
  24.     function ICRStop (var inst: ICRRecord): ICError;
  25.  
  26.     function ICRFindConfigFile (var inst: ICRRecord; count: integer; folders: ICDirSpecArrayPtr): ICError;
  27.     function ICRFindUserConfigFile (var inst: ICRRecord; where: ICDirSpec): ICError;
  28.     function ICRSpecifyConfigFile (var inst: ICRRecord; config: FSSpec): ICError;
  29.  
  30.     function ICRGetSeed (var inst: ICRRecord; var seed: longint): ICError;
  31.     function ICRGetPerm (var inst: ICRRecord; var perm: ICPerm): ICError;
  32.  
  33.     function ICRBegin (var inst: ICRRecord; perm: ICPerm): ICError;
  34.     function ICRGetPref (var inst: ICRRecord; key: Str255; var attr: ICAttr; buf: Ptr; var size: longint): ICError;
  35.     function ICRSetPref (var inst: ICRRecord; key: Str255; attr: ICAttr; buf: Ptr; size: longint): ICError;
  36.     function ICRGetPrefHandle (var inst: ICRRecord; key: Str255; var attr: ICAttr; var prefh: Handle): ICError;
  37.     function ICRSetPrefHandle (var inst: ICRRecord; key: Str255; attr: ICAttr; prefh: Handle): ICError;
  38.     function ICRCountPref (var inst: ICRRecord; var count: longint): ICError;
  39.     function ICRGetIndPref (var inst: ICRRecord; n: longint; var key: Str255): ICError;
  40.     function ICRDeletePref (var inst: ICRRecord; key: Str255): ICError;
  41.     function ICREnd (var inst: ICRRecord): ICError;
  42.     function ICRDefaultFileName (var inst: ICRRecord; var name: Str63): ICError;
  43.  
  44.     function ICREditPreferences (var inst: ICRRecord; key: Str255): ICError;
  45.  
  46.     function ICRParseURL (var inst: ICRRecord; hint: Str255; data: Ptr; len: longint; var selStart, selEnd: longint; url: Handle): ICError;
  47.     function ICRLaunchURL (var inst: ICRRecord; hint: Str255; data: Ptr; len: longint; var selStart, selEnd: longint): ICError;
  48.  
  49.     function ICRMapFilename (var inst: ICRRecord; filename: Str255; var entry: ICMapEntry): ICError;
  50.     function ICRMapTypeCreator (var inst: ICRRecord; fType, fCreator: OSType; filename: Str255; var entry: ICMapEntry): ICError;
  51.  
  52.     function ICRCountMapEntries (var inst: ICRRecord; entries: Handle; var count: longint): ICError;
  53.     function ICRGetIndMapEntry (var inst: ICRRecord; entries: Handle; ndx: longint; var pos: longint; var entry: ICMapEntry): ICError;
  54.     function ICRGetMapEntry (var inst: ICRRecord; entries: Handle; pos: longInt; var entry: ICMapEntry): ICError;
  55.     function ICRSetMapEntry (var inst: ICRRecord; entries: Handle; pos: longInt; var entry: ICMapEntry): ICError;
  56.     function ICRDeleteMapEntry (var inst: ICRRecord; entries: Handle; pos: longint): ICError;
  57.     function ICRAddMapEntry (var inst: ICRRecord; entries: Handle; var entry: ICMapEntry): ICError;
  58.  
  59. (* These are exported solely for the component implementation. *)
  60.     function ICRMapEntriesFilename (var inst: ICRRecord; entries: Handle; filename: Str255; var entry: ICMapEntry): ICError;
  61.     function ICRMapEntriesTypeCreator (var inst: ICRRecord; entries: Handle; fType, fCreator: OSType; filename: Str255; var entry: ICMapEntry): ICError;
  62.  
  63. implementation
  64.  
  65.     uses
  66. {$ifc undefined THINK_Pascal}
  67.         Resources, GestaltEqu, OSUtils, Memory, Errors, ToolUtils, Packages, 
  68. {$endc}
  69.         AppleTalk, Folders, 
  70.  
  71.         ICRSubs;
  72.  
  73.     function ICFindFolder (vRefNum: integer; folderType: OSType; createFolder: boolean; var foundVRefNum: integer; var foundDirID: longint): OSErr;
  74.     inline
  75.         $7000, $A823;
  76.  
  77.     const
  78.         Res_Code = 'ICRP';
  79.  
  80.     function ICRStart (var inst: ICRRecord; creator: OSType): ICError;
  81.         var
  82.             junk: ICError;
  83.     begin
  84.         inst.have_config_file := false;
  85.         inst.config_file.vRefNum := 0;
  86.         inst.config_file.parID := 0;
  87.         inst.config_file.name := '';
  88.         inst.config_refnum := 0;
  89.         inst.perm := icNoPerm;
  90.         junk := ICRDefaultFileName(inst, inst.default_filename);
  91.         ICRStart := noErr;
  92.     end; (* ICRStart *)
  93.  
  94.     procedure ICRCloseIfOpen (var inst: ICRRecord);
  95.     begin
  96.         if inst.config_refnum <> 0 then begin
  97.             CloseResFile(inst.config_refnum);
  98.             inst.config_refnum := 0;
  99.         end; (* if *)
  100.         inst.perm := icNoPerm;
  101.     end; (* ICRCloseIfOpen *)
  102.  
  103.     function ICRStop (var inst: ICRRecord): ICError;
  104.     begin
  105.         ICRCloseIfOpen(inst);
  106.         ICRStop := noErr;
  107.     end; (* ICRStop *)
  108.  
  109.     function ValidDirSpec (folder: ICDirSpec): ICError;
  110.         var
  111.             cpb: CInfoPBRec;
  112.     begin
  113.         cpb.ioVRefNum := folder.vRefNum;
  114.         cpb.ioDirID := folder.dirID;
  115.         cpb.ioNamePtr := nil;
  116.         cpb.ioFDirIndex := -1;
  117.         ValidDirSpec := PBGetCatInfoSync(@cpb);
  118.     end; (* ValidDirSpec *)
  119.  
  120.     function ScanFolder (var inst: ICRRecord; folder: ICDirSpec; var found_file: FSSpec): boolean;
  121.  
  122.         function FoundFile (folder: ICDirSpec; ndx: integer; var found_file: FSSpec): OSErr;
  123.             var
  124.                 err: OSErr;
  125.                 cpb: CInfoPBRec;
  126.                 is_folder: boolean;
  127.                 was_alias: boolean;
  128.                 response: longint;
  129.         begin
  130.             with cpb do begin (* safe *)
  131.                 ioVRefNum := folder.vRefNum;
  132.                 ioDirID := folder.dirID;
  133.                 ioNamePtr := @found_file.name;
  134.                 ioFDirIndex := ndx;
  135.                 err := PBGetCatInfoSync(@cpb);
  136.                 if err = noErr then begin
  137.                     found_file.vRefNum := cpb.ioVRefNum;
  138.                     found_file.parID := cpb.ioFlParID;
  139.                     if (btst(cpb.ioFlAttrib, 4) or (cpb.ioFlFndrInfo.fdType <> ICfiletype)) then begin
  140.                         err := 1;
  141.                     end
  142.                     else if (Gestalt(gestaltAliasMgrAttr, response) = noErr) & btst(response, gestaltAliasMgrPresent) then begin
  143.                         err := ResolveAliasFile(found_file, true, is_folder, was_alias);
  144.                         if err <> noErr then begin
  145.                             err := 1;
  146.                         end; (* if *)
  147.                     end; (* if *)
  148.                 end; (* if *)
  149.             end; (* with *)
  150.             FoundFile := err;
  151.         end; (* FoundFile *)
  152.  
  153.         var
  154.             err: ICError;
  155.             found: boolean;
  156.             i: integer;
  157.     begin
  158.         found_file.name := (inst.default_filename);
  159.         found := (FoundFile(folder, 0, found_file) = noErr);
  160.         if not found then begin
  161.             i := 1;
  162.             repeat
  163.                 found_file.name := '';
  164.                 err := FoundFile(folder, i, found_file);
  165.                 i := i + 1;
  166.             until err <> 1;
  167.             found := (err = noErr);
  168.         end; (* if *)
  169.         ScanFolder := found;
  170.     end; (* ScanFolder *)
  171.  
  172.     function ICRFindConfigFile (var inst: ICRRecord; count: integer; folders: ICDirSpecArrayPtr): ICError;
  173.  
  174.         function FindPrefFolder (var pref_fold: ICDirSpec): OSErr;
  175.             var
  176.                 err: OSErr;
  177.                 env: SysEnvRec;
  178.                 junk: longint;
  179.                 response: longint;
  180.         begin
  181.             if (Gestalt(gestaltFindFolderAttr, response) = noErr) & btst(response, gestaltFindFolderPresent) then begin
  182.                 (* Gestalt says it's implemented -- call it directly *)
  183.                 err := ICFindFolder(kOnSystemDisk, kPreferencesFolderType, true, pref_fold.vRefNum, pref_fold.dirID);
  184.             end
  185.             else begin
  186.                 (* Simulate the important stuff *)
  187.                 err := SysEnvirons(curSysEnvVers, env);
  188.                 if err = noErr then begin
  189.                     err := GetWDInfo(env.sysVRefNum, pref_fold.vRefNum, pref_fold.dirID, junk);
  190.                 end; (* if *)
  191.             end; (* if *)
  192.             FindPrefFolder := err;
  193.         end; (* FindPrefFolder *)
  194.  
  195.         var
  196.             err: ICError;
  197.             i: integer;
  198.             found: boolean;
  199.             pref_fold: ICDirSpec;
  200.     begin
  201.         ICRCloseIfOpen(inst);                { ! }
  202.         err := noErr;
  203.         if (count < 0) | ((count <> 0) & (folders = nil)) then begin
  204.             err := paramErr;
  205.         end; (* if *)
  206.         i := 0;
  207.         while (err = noErr) & (i < count) do begin
  208.             err := ValidDirSpec(folders^[i]);
  209.             i := i + 1;
  210.         end; (* for *)
  211.         if err = noErr then begin
  212.             i := 0;
  213.             found := false;
  214.             while (i < count) and not found do begin
  215.                 found := ScanFolder(inst, folders^[i], inst.config_file);
  216.                 i := i + 1;
  217.             end; (* while *)
  218.             if not found then begin
  219.                 err := FindPrefFolder(pref_fold);
  220.                 if (err = noErr) & not ScanFolder(inst, pref_fold, inst.config_file) then begin
  221.                     inst.config_file.vRefNum := pref_fold.vRefNum;
  222.                     inst.config_file.parID := pref_fold.dirID;
  223.                     inst.config_file.name := inst.default_filename;
  224.                 end; (* if *)
  225.             end; (* if *)
  226.         end; (* if *)
  227.         inst.have_config_file := (err = noErr);
  228.         ICRFindConfigFile := err;
  229.     end; (* ICRFindConfigFile *)
  230.  
  231.     function ICRFindUserConfigFile (var inst: ICRRecord; where: ICDirSpec): ICError;
  232.         var
  233.             err: ICError;
  234.             found: boolean;
  235.     begin
  236.         ICRCloseIfOpen(inst);                { ! }
  237.         err := ValidDirSpec(where);
  238.         if err = noErr then begin
  239.             if not ScanFolder(inst, where, inst.config_file) then begin
  240.                 inst.config_file.vRefNum := where.vRefNum;
  241.                 inst.config_file.parID := where.dirID;
  242.                 inst.config_file.name := inst.default_filename;
  243.             end; (* if *)
  244.         end; (* if *)
  245.         inst.have_config_file := (err = noErr);
  246.         ICRFindUserConfigFile := err;
  247.     end; (* ICRFindUserConfigFile *)
  248.  
  249.     function ICRSpecifyConfigFile (var inst: ICRRecord; config: FSSpec): ICError;
  250.         var
  251.             err: ICError;
  252.             folder: ICDirSpec;
  253.     begin
  254.         ICRCloseIfOpen(inst);                { ! }
  255.         folder.vRefNum := config.vRefNum;
  256.         folder.dirID := config.parID;
  257.         err := ValidDirSpec(folder);
  258.         if err = noErr then begin
  259.             inst.config_file := config;
  260.         end; (* if *)
  261.         inst.have_config_file := (err = noErr);
  262.         ICRSpecifyConfigFile := err;
  263.     end; (* ICRSpecifyConfigFile *)
  264.  
  265.     function ICRGetSeed (var inst: ICRRecord; var seed: longint): ICError;
  266.         var
  267.             err: ICError;
  268.             cpb: CInfoPBRec;
  269.     begin
  270.         seed := 0;
  271.         err := fnfErr;
  272.         if inst.have_config_file then begin
  273.             with cpb do begin (* safe *)
  274.                 ioVRefNum := inst.config_file.vRefNum;
  275.                 ioDirID := inst.config_file.parID;
  276.                 ioNamePtr := @inst.config_file.name;
  277.                 ioFDirIndex := 0;
  278.             end; (* with *)
  279.             err := PBGetCatInfoSync(@cpb);
  280.             if err = noErr then begin
  281.                 seed := cpb.ioFlMdDat;
  282.             end
  283.             else if err = fnfErr then begin
  284.                 err := noErr;
  285.             end; (* if *)
  286.         end; (* if *)
  287.         ICRGetSeed := err;
  288.     end; (* ICRGetSeed *)
  289.  
  290.     function ICRGetPerm (var inst: ICRRecord; var perm: ICPerm): ICError;
  291.     begin
  292.         perm := inst.perm;
  293.         ICRGetPerm := noErr;
  294.     end; (* ICRGetPerm *)
  295.  
  296.     function ICRPermToFSPerm (perm: ICPerm): integer;
  297.     begin
  298.         case perm of
  299.             icReadOnlyPerm: 
  300.                 ICRPermToFSPerm := fsRdPerm;
  301.             icReadWritePerm: 
  302.                 ICRPermToFSPerm := fsRdWrPerm;
  303.             otherwise
  304.                 ICRPermToFSPerm := 0;
  305.         end; (* case *)
  306.     end; (* ICRPermToFSPerm *)
  307.  
  308.     function ICRBegin (var inst: ICRRecord; perm: ICPerm): ICError;
  309.         var
  310.             err: ICError;
  311.             ref: integer;
  312.             junk: OSErr;
  313.     begin
  314.         err := noErr;
  315.         if (inst.perm <> icNoPerm) or (perm = icNoPerm) then begin
  316.             err := paramErr;
  317.         end; (* if *)
  318.         if err = noErr then begin
  319.             ICRCloseIfOpen(inst);                { ! }
  320.             if not inst.have_config_file then begin
  321.                 err := bdNamErr;
  322.             end; (* if *)
  323.         end; (* if *)
  324.         if err = noErr then begin
  325.             ref := HOpenResFile(inst.config_file.vRefNum, inst.config_file.parID, inst.config_file.name, ICRPermToFSPerm(perm));
  326.             err := ResError;
  327.             if (err = fnfErr) or (err = eofErr) then begin
  328.                 case perm of
  329.                     icReadOnlyPerm:  begin
  330.                         ref := 0;
  331.                         err := noErr;
  332.                     end; (* icReadOnlyPerm *)
  333.                     icReadWritePerm:  begin
  334.                         junk := HCreate(inst.config_file.vRefNum, inst.config_file.parID, inst.config_file.name, ICcreator, ICfiletype);
  335.                         HCreateResFile(inst.config_file.vRefNum, inst.config_file.parID, inst.config_file.name);
  336.                         ref := HOpenResFile(inst.config_file.vRefNum, inst.config_file.parID, inst.config_file.name, ICRPermToFSPerm(perm));
  337.                         err := ResError;
  338.                     end; (* icReadWritePerm *)
  339.                 end; (* case *)
  340.             end; (* if *)
  341.         end; (* if *)
  342.         if err = noErr then begin
  343.             inst.config_refnum := ref;
  344.             inst.perm := perm;
  345.         end; (* if *)
  346.         case err of
  347.             opWrErr, permErr: 
  348.                 err := icNoMoreWritersErr;
  349.             otherwise { do nothing }
  350.         end; (* case *)
  351.         ICRBegin := err;
  352.     end; (* ICRBegin *)
  353.  
  354.     function ICRCheckInside (var inst: ICRRecord): ICError;
  355.     begin
  356.         if inst.perm = icNoPerm then begin
  357.             ICRCheckInside := paramErr;
  358.         end
  359.         else begin
  360.             ICRCheckInside := noErr;
  361.         end; (* if *)
  362.     end; (* ICRCheckInside *)
  363.  
  364.     function ICRForceInside (var inst: ICRRecord; perm: ICPerm; var force_info: boolean): ICError;
  365.         var
  366.             err: ICError;
  367.     begin
  368.         force_info := false;
  369.         if (inst.perm = perm) or ((inst.perm = icReadWritePerm) and (perm = icReadOnlyPerm)) then begin
  370.             err := noErr;
  371.         end
  372.         else if inst.perm = icNoPerm then begin
  373.             err := ICRBegin(inst, perm);
  374.             force_info := (err = noErr);
  375.         end
  376.         else begin
  377.             err := icPermErr;
  378.         end; (* if *)
  379.         ICRForceInside := err;
  380.     end; (* ICRForceInside *)
  381.  
  382.     function ICRReleaseInside (var inst: ICRRecord; force_info: boolean): ICError;
  383.     begin
  384.         if force_info then begin
  385.             ICRReleaseInside := ICREnd(inst);
  386.         end
  387.         else begin
  388.             ICRReleaseInside := noErr;
  389.         end; (* if *)
  390.     end; (* ICRReleaseInside *)
  391.  
  392.     function ICRGetPref (var inst: ICRRecord; key: Str255; var attr: ICAttr; buf: Ptr; var size: longint): ICError;
  393.         var
  394.             err: ICError;
  395.             err2: ICError;
  396.             max_size: longint;
  397.             true_size: longint;
  398.             old_refnum: integer;
  399.             prefh: Handle;
  400.             force_info: boolean;
  401.     begin
  402.         max_size := size;
  403.         size := 0;
  404.         attr := ICattr_no_change;
  405.         prefh := nil;
  406.         err := ICRForceInside(inst, icReadOnlyPerm, force_info);
  407.         if (err = noErr) and (inst.config_refnum = 0) then begin
  408.             err := icPrefNotFoundErr;
  409.         end; (* if *)
  410.         if (err = noErr) and ((key = '') or ((max_size < 0) and (buf <> nil))) then begin
  411.             err := paramErr;
  412.         end; (* if *)
  413.         if err = noErr then begin
  414.             old_refnum := CurResFile;
  415.             UseResFile(inst.config_refnum);
  416.             err := ResError;
  417.             if err = noErr then begin
  418.                 prefh := Get1NamedResource(Res_Code, key);
  419.                 err := ResError;
  420.                 if prefh = nil then begin
  421.                     err := icPrefNotFoundErr;
  422.                 end; (* if *)
  423.                 if err = noErr then begin
  424.                     true_size := GetHandleSize(prefh);
  425.                     if true_size < 4 then begin
  426.                         err := icPrefDataErr;
  427.                     end; (* if *)
  428.                 end; (* if *)
  429.                 if err = noErr then begin
  430.                     size := true_size - 4;
  431.                     attr := longintPtr(prefh^)^;
  432.                     if (buf <> nil) and (size <> 0) then begin
  433.                         if size > max_size then begin
  434.                             err := icTruncatedErr;
  435.                         end
  436.                         else begin
  437.                             max_size := size;
  438.                         end; (* if *)
  439.                         BlockMove(ptr(longint(prefh^) + 4), buf, max_size);
  440.                     end; (* if *)
  441.                 end; (* if *)
  442.                 UseResFile(old_refnum);
  443.             end; (* if *)
  444.         end; (* if *)
  445.         if prefh <> nil then begin
  446.             ReleaseResource(prefh);
  447.         end; (* if *)
  448.         err2 := ICRReleaseInside(inst, force_info);
  449.         if err = noErr then begin
  450.             err := err2;
  451.         end; (* if *)
  452.         ICRGetPref := err;
  453.     end; (* ICRGetPref *)
  454.  
  455.     function ICRSetPref (var inst: ICRRecord; key: Str255; attr: ICAttr; buf: Ptr; size: longint): ICError;
  456.         var
  457.             err: ICError;
  458.             err2: ICError;
  459.             old_attr: longint;
  460.             old_refnum: integer;
  461.             prefh: Handle;
  462.             id: integer;
  463.             force_info: boolean;
  464.     begin
  465.         prefh := nil;
  466.         if buf = nil then begin
  467.             size := 0;
  468.         end;
  469.         err := ICRForceInside(inst, icReadWritePerm, force_info);
  470.         if (err = noErr) and (inst.perm <> icReadWritePerm) then begin
  471.             err := icPermErr;
  472.         end; (* if *)
  473.         if (err = noErr) and (inst.config_refnum = 0) then begin
  474.             err := icInternalErr;
  475.         end; (* if *)
  476.         if (err = noErr) and ((key = '') or (size < 0)) then begin
  477.             err := paramErr;
  478.         end; (* if *)
  479.         if err = noErr then begin
  480.             old_refnum := CurResFile;
  481.             UseResFile(inst.config_refnum);
  482.             err := ResError;
  483.             if err = noErr then begin
  484.                 prefh := Get1NamedResource(Res_Code, key);
  485.                 if (prefh <> nil) & (GetHandleSize(prefh) < 4) then begin { very bad! }
  486.                     RmveResource(prefh);
  487.                     DisposeHandle(prefh);
  488.                     prefh := nil;
  489.                 end;
  490.                 if (prefh = nil) then begin
  491.                     old_attr := 0;
  492.                 end
  493.                 else begin
  494.                     old_attr := longintPtr(prefh^)^;
  495.                 end;
  496.                 if attr = ICattr_no_change then begin
  497.                     attr := old_attr;
  498.                 end; (* if *)
  499.                 if btst(old_attr, ICattr_locked_bit) and btst(attr, ICattr_locked_bit) and (buf <> nil) then begin
  500.                     err := icPermErr;
  501.                 end; (* if *)
  502.                 if (prefh = nil) then begin
  503.                     prefh := NewHandle(size + 4);
  504.                     err := MemError;
  505.                     if err = noErr then begin
  506.                         repeat
  507.                             id := Unique1ID(Res_Code);
  508.                         until id > 127;
  509.                         AddResource(prefh, Res_Code, id, key);
  510.                         err := ResError;
  511.                         if err <> noErr then begin
  512.                             DisposeHandle(prefh);
  513.                             prefh := nil;
  514.                         end; (* if *)
  515.                     end; (* if *)
  516.                 end; (* if *)
  517.                 if (err = noErr) & (buf <> nil) then begin
  518.                     SetHandleSize(prefh, size + 4);
  519.                     err := MemError;
  520.                 end; (* if *)
  521.                 if (err = noErr) & (size > 0) then begin
  522.                     BlockMove(buf, ptr(longint(prefh^) + 4), size);
  523.                 end; (* if *)
  524.                 if (err = noErr) then begin
  525.                     longintPtr(prefh^)^ := attr;
  526.                     ChangedResource(prefh);
  527.                     WriteResource(prefh);
  528.                     err := ResError;
  529.                 end; (* if *)
  530.                 UseResFile(old_refnum);
  531.             end; (* if *)
  532.         end; (* if *)
  533.         if prefh <> nil then begin
  534.             ReleaseResource(prefh);
  535.         end; (* if *)
  536.         err2 := ICRReleaseInside(inst, force_info);
  537.         if err = noErr then begin
  538.             err := err2;
  539.         end; (* if *)
  540.         ICRSetPref := err;
  541.     end; (* ICRSetPref *)
  542.  
  543.     (* I call ICRForceInside to speed this routine up.  ICRForceInside will do an ICRBegin and hence open the resource *)
  544.     (* file, which is good because otherwise I'd open it twice, once for each ICRGetPref. *)
  545.  
  546.     function ICRGetPrefHandle (var inst: ICRRecord; key: Str255; var attr: ICAttr; var prefh: Handle): ICError;
  547.         var
  548.             err: ICError;
  549.             prefsize: longint;
  550.             force_info: boolean;
  551.             err2: ICError;
  552.     begin
  553.         prefh := nil;
  554.         prefsize := 0;
  555.         err := ICRForceInside(inst, icReadOnlyPerm, force_info);
  556.         if err = noErr then begin
  557.             err := ICRGetPref(inst, key, attr, nil, prefsize);
  558.         end; (* if *)
  559.         if err <> noErr then begin
  560.             prefsize := 0;
  561.         end; (* if *)
  562.         prefh := NewHandle(prefsize);
  563.         err := MemError;
  564.         if err = noErr then begin
  565.             HLock(prefh);
  566.             err := ICRGetPref(inst, key, attr, prefh^, prefsize);
  567.             if err = icPrefNotFoundErr then begin
  568.                 attr := 0;
  569.                 err := noErr;
  570.             end; (* if *)
  571.             HUnlock(prefh);
  572.         end; (* if *)
  573.         if err <> noErr then begin
  574.             if prefh <> nil then begin
  575.                 DisposeHandle(prefh);
  576.             end; (* if *)
  577.             prefh := nil;
  578.         end; (* if *)
  579.         err2 := ICRReleaseInside(inst, force_info);
  580.         if err = noErr then begin
  581.             err := err2;
  582.         end; (* if *)
  583.         ICRGetPrefHandle := err;
  584.     end; (* ICRGetPrefHandle *)
  585.  
  586.     function ICRSetPrefHandle (var inst: ICRRecord; key: Str255; attr: ICAttr; prefh: Handle): ICError;
  587.         var
  588.             s: SignedByte;
  589.             err: ICError;
  590.     begin
  591.         err := noErr;
  592.         if prefh <> nil then begin
  593.             if prefh^ = nil then begin
  594.                 err := paramErr;
  595.             end; (* if *)
  596.             if err = noErr then begin
  597.                 s := HGetState(prefh);
  598.                 HLock(prefh);
  599.                 err := ICRSetPref(inst, key, attr, prefh^, GetHandleSize(prefh));
  600.                 HSetState(prefh, s);
  601.             end; (* if *)
  602.         end else begin
  603.             err := ICRSetPref(inst, key, attr, nil, 0);
  604.         end; (* if *)
  605.         ICRSetPrefHandle := err;
  606.     end; (* ICRSetPrefHandle *)
  607.  
  608.     function ICRCountPref (var inst: ICRRecord; var count: longint): ICError;
  609.         var
  610.             err: ICError;
  611.             old_refnum: integer;
  612.     begin
  613.         err := ICRCheckInside(inst);
  614.         if err = noErr then begin
  615.             if inst.config_refnum = 0 then begin
  616.                 count := 0;
  617.             end
  618.             else begin
  619.                 old_refnum := CurResFile;
  620.                 UseResFile(inst.config_refnum);
  621.                 err := ResError;
  622.                 if err = noErr then begin
  623.                     count := Count1Resources(Res_Code);
  624.                     err := ResError;
  625.                     UseResFile(old_refnum);
  626.                 end; (* if *)
  627.             end; (* if *)
  628.         end; (* if *)
  629.         if err <> noErr then begin
  630.             count := 0;
  631.         end; (* if *)
  632.         ICRCountPref := err;
  633.     end; (* ICRCountPref *)
  634.  
  635.     function ICRGetIndPref (var inst: ICRRecord; n: longint; var key: Str255): ICError;
  636.         var
  637.             err: ICError;
  638.             old_refnum: integer;
  639.             prefh: Handle;
  640.             junk_id: integer;
  641.             junk_type: ResType;
  642.     begin
  643.         prefh := nil;
  644.         err := ICRCheckInside(inst);
  645.         if (err = noErr) and (n < 1) then begin
  646.             err := paramErr;
  647.         end; (* if *)
  648.         if err = noErr then begin
  649.             if inst.config_refnum = 0 then begin
  650.                 err := icPrefNotFoundErr;
  651.             end
  652.             else begin
  653.                 old_refnum := CurResFile;
  654.                 UseResFile(inst.config_refnum);
  655.                 err := ResError;
  656.                 if err = noErr then begin
  657.                     SetResLoad(false);
  658.                     prefh := Get1IndResource(Res_Code, n);
  659.                     SetResLoad(true);
  660.                     if prefh = nil then begin
  661.                         err := icPrefNotFoundErr;
  662.                     end
  663.                     else begin
  664.                         GetResInfo(prefh, junk_id, junk_type, key);
  665.                         err := ResError;
  666.                     end; (* if *)
  667.                     UseResFile(old_refnum);
  668.                 end; (* if *)
  669.             end; (* if *)
  670.         end; (* if *)
  671.         if prefh <> nil then begin
  672.             ReleaseResource(prefh);
  673.         end; (* if *)
  674.         ICRGetIndPref := err;
  675.     end; (* ICRGetIndPref *)
  676.  
  677.     function ICRDeletePref (var inst: ICRRecord; key: Str255): ICError;
  678.         var
  679.             err: ICError;
  680.             prefh: Handle;
  681.             old_refnum: integer;
  682.     begin
  683.         err := ICRCheckInside(inst);
  684.         if (err = noErr) and (key = '') then begin
  685.             err := paramErr;
  686.         end; (* if *)
  687.         if err = noErr then begin
  688.             if inst.config_refnum = 0 then begin
  689.                 err := icPrefNotFoundErr;
  690.             end
  691.             else begin
  692.                 old_refnum := CurResFile;
  693.                 UseResFile(inst.config_refnum);
  694.                 err := ResError;
  695.                 if err = noErr then begin
  696.                     SetResLoad(false);
  697.                     prefh := Get1NamedResource(Res_Code, key);
  698.                     err := ResError;
  699.                     SetResLoad(true);
  700.                     if prefh = nil then begin
  701.                         err := icPrefNotFoundErr;
  702.                     end; (* if *)
  703.                     if err = noErr then begin
  704.                         RmveResource(prefh);
  705.                         err := ResError;
  706.                     end; (* if *)
  707.                     UseResFile(old_refnum);
  708.                 end; (* if *)
  709.             end; (* if *)
  710.         end; (* if *)
  711.         ICRDeletePref := err;
  712.     end; (* ICRDeletePref *)
  713.  
  714.     function ICREnd (var inst: ICRRecord): ICError;
  715.         var
  716.             err: ICError;
  717.     begin
  718.         err := ICRCheckInside(inst);
  719.         ICRCloseIfOpen(inst);
  720.         ICREnd := err;
  721.     end; (* ICREnd *)
  722.  
  723.     function ICRDefaultFileName (var inst: ICRRecord; var name: Str63): ICError;
  724.     begin
  725.         name := ICdefault_file_name;
  726.         ICRDefaultFileName := noErr;
  727.     end; (* ICRDefaultFileName *)
  728.  
  729.     function ICREditPreferences (var inst: ICRRecord; key: Str255): ICError;
  730.         var
  731.             err: ICError;
  732.     begin
  733.         err := noErr;
  734.         if not inst.have_config_file then begin
  735.             err := bdNamErr;
  736.         end; (* if *)
  737.         if err = noErr then begin
  738.             err := EditPreferences(key, inst.config_file);
  739.         end; (* if *)
  740.         ICREditPreferences := err;
  741.     end; (* ICREditPreferences *)
  742.  
  743. (* URL Parsing Algorithm *)
  744.  
  745. {1. if there is a selection skip to step 4}
  746. {2. expand selection to end of word (never skip an angle bracket though) }
  747. {3. if either end has an angle bracket then expand other end to search for angle bracket}
  748. {4. strip trailing and leading whitespace}
  749. {5. strip whitespace CR whitespace}
  750. {6. off < > if necessary}
  751. {7. remove leading URL:}
  752. {8. extract protocol by looking forwards for :}
  753. {9. if no protocol then prepend "hint:"}
  754.  
  755.     const
  756.         verybig = 100000;
  757.     type
  758.         dataArray = packed array[0..verybig] of char;
  759.         dataPtr = ^dataArray;
  760.         dataHandle = ^dataPtr;
  761.  
  762.     function ExpandSelection (datap: dataPtr; len: longint; var selStart, selEnd: longint): ICError;
  763.         var
  764.             err: ICError;
  765.             found: boolean;
  766.     begin
  767.         err := noErr;
  768.         (* expand leading selection backwards looking for word break *)
  769.         while (selStart > 0) & not (datap^[selStart - 1] in [' ', '<', chr(9), chr(13)]) do begin
  770.             selStart := selStart - 1;
  771.         end; (* while *)
  772.         if (selStart > 0) & (datap^[selStart - 1] = '<') then begin
  773.             selStart := selStart - 1;
  774.         end; (* if *)
  775.         (* expand trailing selection forwards looking for work break *)
  776.         while (selEnd < len) & not (datap^[selEnd] in [' ', '>', chr(9), chr(13)]) do begin
  777.             selEnd := selEnd + 1;
  778.         end; (* while *)
  779.         if (selEnd < len) & (datap^[selEnd] = '>') then begin
  780.             selEnd := selEnd + 1;
  781.         end; (* if *)
  782.         (* if first character was a < then expand trailing selection to meet matching > *)
  783.         if datap^[selStart] = '<' then begin
  784.             found := false;
  785.             while not found and (selEnd - 1 < len) do begin
  786.                 found := (datap^[selEnd - 1] = '>');
  787.                 if not found then begin
  788.                     selEnd := selEnd + 1;
  789.                 end; (* if *)
  790.             end; (* while *)
  791.             if not found then begin
  792.                 err := icNoURLErr;
  793.             end; (* if *)
  794.         end; (* if *)
  795.         (* if last character was a > then expand leading selection to meet matching < *)
  796.         if (err = noErr) & (selEnd > 0) & (datap^[selEnd - 1] = '>') then begin
  797.             found := (datap^[selStart] = '<');
  798.             while not found and (selStart >= 0) do begin
  799.                 found := (datap^[selStart] = '<');
  800.                 if not found then begin
  801.                     selStart := selStart - 1;
  802.                 end; (* if *)
  803.             end; (* if *)
  804.             if not found then begin
  805.                 err := icNoURLErr;
  806.             end; (* if *)
  807.         end; (* if *)
  808.         ExpandSelection := err;
  809.     end; (* ExpandSelection *)
  810.  
  811.     function ShrinkSelection (datap: dataPtr; len: longint; var selStart, selEnd: longint): ICError;
  812.     begin
  813.         (* strip leading whitespace *)
  814.         while (selStart < len) & (datap^[selStart] in [' ', chr(9)]) do begin
  815.             selStart := selStart + 1;
  816.         end; (* while *)
  817.         (* strip trailing whitespace *)
  818.         while (selEnd > 0) & (datap^[selEnd - 1] in [' ', chr(9)]) do begin
  819.             selEnd := selEnd - 1;
  820.         end; (* while *)
  821.         ShrinkSelection := noErr;
  822.     end; (* ShrinkSelection *)
  823.  
  824.     function StripReturns (urlh: dataHandle): ICError;
  825.             (* removes any sequence of <whitespace> <cr> <whitespace> from urlh *)
  826.         var
  827.             srcsize: longint;
  828.             srcndx: longint;
  829.             dstndx: longint;
  830.             err: ICError;
  831.     begin
  832.         srcsize := GetHandleSize(Handle(urlh));
  833.         srcndx := 0;
  834.         dstndx := 0;
  835.             (* skip down the handle copying src to dst except when meeting cr *)
  836.         while srcndx < srcsize do begin
  837.             if urlh^^[srcndx] = chr(13) then begin
  838.                     (* move dstndx back to point to previous non-whitespace *)
  839.                 while (dstndx > 0) & (urlh^^[dstndx - 1] in [' ', chr(9)]) do begin
  840.                     dstndx := dstndx - 1;
  841.                 end; (* while *)
  842.                     (* move srcndx forwards to next non-whitespace *)
  843.                 while (srcndx < srcsize) & (urlh^^[srcndx] in [' ', chr(9), chr(13)]) do begin
  844.                     srcndx := srcndx + 1;
  845.                 end; (* while *)
  846.             end; (* case *)
  847.             if srcndx < srcsize then begin
  848.                     (* copy a byte from src to dst *)
  849.                 urlh^^[dstndx] := urlh^^[srcndx];
  850.                 srcndx := srcndx + 1;
  851.                 dstndx := dstndx + 1;
  852.             end; (* if *)
  853.         end; (* while *)
  854.             (* resize the handle to the number of bytes that we copied *)
  855.         SetHandleSize(Handle(urlh), dstndx);
  856.         err := MemError;
  857.         if (err = noErr) & (GetHandleSize(Handle(urlh)) = 0) then begin
  858.             err := icNoURLErr;
  859.         end; (* if *)
  860.         StripReturns := err;
  861.     end; (* StripReturns *)
  862.  
  863.     function ICRParseURL (var inst: ICRRecord; hint: Str255; data: Ptr; len: longint; var selStart, selEnd: longint; url: Handle): ICError;
  864.         var
  865.             datap: dataPtr;
  866.             urlh: dataHandle;
  867.             tmp: Str15;
  868.             junklong: longint;
  869.             ndx: longint;
  870.             err: ICError;
  871.     begin
  872.         datap := dataPtr(data);
  873.         urlh := dataHandle(url);
  874.         err := noErr;
  875.         if (data = nil) | (url = nil) | (url^ = nil) | (len <= 0) | (selStart < 0) | (selEnd < 0) | (selStart > len) | (selEnd > len) | (selStart > selEnd) then begin
  876.             err := paramErr;
  877.         end; (* if *)
  878.         if (err = noErr) and (selStart = selEnd) then begin
  879.             err := ExpandSelection(datap, len, selStart, selEnd);
  880.         end; (* if *)
  881.         if err = noErr then begin
  882.             (* remove leading and trailing whitespace sequences *)
  883.             err := ShrinkSelection(datap, len, selStart, selEnd);
  884.         end; (* if *)
  885.         if (err = noErr) and (selStart >= selEnd) then begin
  886.             err := icNoURLErr;
  887.         end; (* if *)
  888.         if err = noErr then begin
  889.             (* copy the selection out into url *)
  890.             err := PtrToXHand(@datap^[selStart], url, selEnd - selStart);
  891.         end; (* if *)
  892.         if err = noErr then begin
  893.             (* remove any <whitespace> <cr> <whitespace> sequences *)
  894.             err := StripReturns(urlh);
  895.         end; (* if *)
  896.         if err = noErr then begin
  897.             (* trip any enclosing < > *)
  898.             if (urlh^^[0] = '<') and (urlh^^[GetHandleSize(Handle(urlh)) - 1] = '>') then begin
  899.                 SetHandleSize(Handle(urlh), GetHandleSize(Handle(urlh)) - 1);        (* trim off tail *)
  900.                 junklong := Munger(Handle(urlh), 0, nil, 1, Ptr(-1), 0);    (* trim off first character *)
  901.             end; (* if *)
  902.             (* trim off leading "URL:" *)
  903.             tmp := 'URL:';
  904.             HLock(Handle(urlh));
  905.             if (GetHandleSize(Handle(urlh)) >= length(tmp)) & (IUMagString(Ptr(urlh^), @tmp[1], length(tmp), length(tmp)) = 0) then begin
  906.                 HUnlock(Handle(urlh));        (* unlock 'cause Munger is going to want it that way *)
  907.                 junklong := Munger(Handle(urlh), 0, nil, 4, Ptr(-1), 0);    (* trim off 'URL:' character *)
  908.             end;
  909.             HUnlock(Handle(urlh));
  910.             (* search for protocol *)
  911.             tmp := ':';
  912.             ndx := Munger(Handle(urlh), 0, @tmp[1], length(tmp), nil, 0);
  913.             if (ndx < 0) or (ndx > 255) then begin
  914.                 (* failed to find : in first 256 bytes, prepend "hint:" to URL *)
  915.                 if hint = '' then begin
  916.                     err := icNoURLErr;
  917.                 end
  918.                 else begin
  919.                     hint := concat(hint, ':');
  920.                     junklong := Munger(Handle(urlh), 0, nil, 0, @hint[1], length(hint));
  921.                     err := MemError;
  922.                 end; (* if *)
  923.             end; (* if *)
  924.         end; (* if *)
  925.         ICRParseURL := err;
  926.     end; (* ICRParseURL *)
  927.  
  928.     function ICRLaunchURL (var inst: ICRRecord; hint: Str255; data: Ptr; len: longint; var selStart, selEnd: longint): ICError;
  929.         var
  930.             err: ICError;
  931.             urlh: Handle;
  932.             helper: ICAppSpec;
  933.             scheme: Str255;
  934.             junk_attr: longint;
  935.             size: longint;
  936.     begin
  937.         urlh := NewHandle(0);
  938.         err := MemError;
  939.         if err = noErr then begin
  940.             err := ICRParseURL(inst, hint, data, len, selStart, selEnd, urlh);
  941.         end; (* if *)
  942.         if err = noErr then begin
  943.             err := FindScheme(urlh, scheme);
  944.         end; (* if *)
  945.         if err = noErr then begin
  946.             size := sizeof(helper);
  947.             err := ICRGetPref(inst, concat(kICHelper, scheme), junk_attr, @helper, size);
  948.         end; (* if *)
  949.         if err = noErr then begin
  950.             err := LaunchURL(helper.fCreator, urlh);
  951.         end; (* if *)
  952.         if urlh <> nil then begin
  953.             DisposeHandle(urlh);
  954.         end; (* if *)
  955.         ICRLaunchURL := err;
  956.     end; (* ICRLaunchURL *)
  957.  
  958. (* Internal Mapping Subs *)
  959.  
  960.     function UnpackEntry (entries: handle; pos: longInt; var entry: ICMapEntry; var user_length: longInt): OSErr;
  961. (* WARNING: Depends very much on the exact format of ICMapEntry! *)
  962.         procedure CopyString (var p: ptr; var s: str255);
  963.             var
  964.                 len: integer;
  965.         begin
  966.             len := BAND(p^, $FF) + 1;
  967.             BlockMove(p, @s, len);
  968.             p := ptr(ord(p) + len);
  969.         end;
  970.         var
  971.             org: Ptr;
  972.             p: ptr;
  973.             maxsize: longInt;
  974.             err: OSErr;
  975.     begin
  976.         err := noErr;
  977.         if (entries = nil) | (entries^ = nil) | (pos < 0) | (pos > GetHandleSize(entries) - 6) then begin
  978.             err := paramErr;
  979.         end;
  980.         if err = noErr then begin
  981.             p := (ptr(ord(entries^) + pos));
  982.             maxsize := GetHandleSize(entries);
  983.             org := p;
  984.             BlockMove(p, @entry, 6);
  985.             if (entry.fixed_length <> ICmap_fixed_length) | (entry.fixed_length > entry.total_length) | (entry.total_length > maxsize) then begin
  986.                 err := badExtResource;
  987.             end;
  988.         end;
  989.         if err = noErr then begin
  990.             BlockMove(p, @entry, entry.fixed_length);
  991.             p := ptr(ord(p) + entry.fixed_length);
  992.             CopyString(p, entry.extension);
  993.             CopyString(p, entry.creator_app_name);
  994.             CopyString(p, entry.post_app_name);
  995.             CopyString(p, entry.MIME_type);
  996.             CopyString(p, entry.entry_name);
  997.             user_length := entry.total_length - (ord(p) - ord(org));
  998.         end;
  999.         UnpackEntry := err;
  1000.     end;
  1001.  
  1002.     function FastGetEntry (entries: Handle; pos: longint; var entry: ICMapEntry): OSErr;
  1003. (* A fast version of ICRGetEntry, doesn't return all of the strings in the entry. *)
  1004. (* WARNING: Depends very much on the exact format of ICMapEntry! *)
  1005.         var
  1006.             org: Ptr;
  1007.             p: ptr;
  1008.             maxsize: longInt;
  1009.             err: OSErr;
  1010.     begin
  1011.         err := noErr;
  1012.         if (entries = nil) | (entries^ = nil) | (pos < 0) | (pos > GetHandleSize(entries) - 6) then begin
  1013.             err := paramErr;
  1014.         end;
  1015.         if err = noErr then begin
  1016.             p := (ptr(ord(entries^) + pos));
  1017.             maxsize := GetHandleSize(entries);
  1018.             BlockMove(p, @entry, 6);
  1019.             if (entry.fixed_length <> ICmap_fixed_length) | (entry.fixed_length > entry.total_length) | (entry.total_length > maxsize) then begin
  1020.                 err := badExtResource;
  1021.             end;
  1022.         end;
  1023.         if err = noErr then begin
  1024.             BlockMove(p, @entry, entry.fixed_length);
  1025.             p := ptr(ord(p) + entry.fixed_length);
  1026.             BlockMove(p, @entry.extension, band(p^, $00FF) + 1);
  1027.         end;
  1028.         FastGetEntry := err;
  1029.     end; (* FastGetEntry *)
  1030.  
  1031.     procedure PackEntry (var entry: ICMapEntry; p: ptr; user_length: longInt);
  1032.         procedure CopyString (var s: str255);
  1033.         begin
  1034.             BlockMove(@s, ptr(ord(p) + entry.total_length), length(s) + 1);
  1035.             entry.total_length := entry.total_length + length(s) + 1;
  1036.         end;
  1037.     begin
  1038.         entry.version := 0;
  1039.         entry.fixed_length := ord(@entry.extension) - ord(@entry);
  1040.         entry.total_length := entry.fixed_length;
  1041.         CopyString(entry.extension);
  1042.         CopyString(entry.creator_app_name);
  1043.         CopyString(entry.post_app_name);
  1044.         CopyString(entry.MIME_type);
  1045.         CopyString(entry.entry_name);
  1046.         entry.total_length := entry.total_length + user_length;
  1047.         BlockMove(@entry, p, entry.fixed_length);
  1048.     end;
  1049.  
  1050.     function GetShort (p: Ptr): integer;
  1051.     begin
  1052.         GetShort := BAND(p^, $FF) * 256 + BAND(ptr(ord(p) + 1)^, $FF);
  1053.     end;
  1054.  
  1055.     function UpCase (ch: char): char;
  1056.     inline
  1057.         $301F, $0C00, $0061, $6500, $000E, $0C00, $007B, $6400, $0006, $0400, $0020, $3E80;
  1058.  
  1059.     function IsExtensionVar (var name, ext: str255): boolean;
  1060.         var
  1061.             pn, pe: integer;
  1062.     begin
  1063.         IsExtensionVar := false;
  1064.         if length(name) >= length(ext) then begin
  1065.             pn := length(name) - length(ext) + 1;
  1066.             pe := 1;
  1067.             while pe <= length(ext) do begin
  1068.                 if UpCase(name[pn]) <> UpCase(ext[pe]) then begin
  1069.                     leave;
  1070.                 end; (* if *)
  1071.                 pn := pn + 1;
  1072.                 pe := pe + 1;
  1073.             end; (* while *)
  1074.             IsExtensionVar := (pe > length(ext));
  1075.         end; (* if *)
  1076.     end; (* IsExtensionVar *)
  1077.  
  1078. (* Low Level Mapping Routines *)
  1079.  
  1080.     function ICRCountMapEntries (var inst: ICRRecord; entries: Handle; var count: longint): ICError;
  1081.         var
  1082.             err: ICError;
  1083.             p: Ptr;
  1084.             pos: longint;
  1085.             size: integer;
  1086.     begin
  1087.         err := noErr;
  1088.         if (entries = nil) | (entries^ = nil) then begin
  1089.             err := paramErr;
  1090.         end; (* if *)
  1091.         if err = noErr then begin
  1092.             p := entries^;
  1093.             pos := 0;
  1094.             count := 0;
  1095.             while pos < GetHandleSize(entries) do begin
  1096.                 size := GetShort(p);
  1097.                 pos := pos + size;
  1098.                 p := ptr(ord(p) + size);
  1099.                 count := count + 1;
  1100.             end; (* while *)
  1101.         end; (* if *)
  1102.         ICRCountMapEntries := err;
  1103.     end; (* ICRCountMapEntries *)
  1104.  
  1105.     function ICRGetIndMapEntry (var inst: ICRRecord; entries: handle; ndx: longint; var pos: longint; var entry: ICMapEntry): ICError;
  1106.         var
  1107.             err: ICError;
  1108.             p: Ptr;
  1109.             i: longint;
  1110.             size: integer;
  1111.     begin
  1112.         err := noErr;
  1113.         if (entries = nil) | (entries^ = nil) | (ndx < 0) then begin
  1114.             err := paramErr;
  1115.         end; (* if *)
  1116.         if err = noErr then begin
  1117.             p := entries^;
  1118.             pos := 0;
  1119.             while (ndx > 1) & (pos < GetHandleSize(entries)) do begin
  1120.                 size := GetShort(p);
  1121.                 pos := pos + size;
  1122.                 p := Ptr(ord(p) + size);
  1123.                 ndx := ndx - 1;
  1124.             end; (* while *)
  1125.             err := ICRGetMapEntry(inst, entries, pos, entry);
  1126.         end; (* if *)
  1127.         ICRGetIndMapEntry := err;
  1128.     end; (* ICRGetIndMapEntry *)
  1129.  
  1130.     function ICRGetMapEntry (var inst: ICRRecord; entries: handle; pos: longInt; var entry: ICMapEntry): ICError;
  1131.         var
  1132.             err: ICError;
  1133.             user_length: longInt;
  1134.     begin
  1135.         err := noErr;
  1136.         if (entries = nil) | (entries^ = nil) | (pos < 0) | (pos >= GetHandleSize(entries)) then begin
  1137.             err := paramErr;
  1138.         end; (* if *)
  1139.         if err = noErr then begin
  1140.             err := UnpackEntry(entries, pos, entry, user_length);
  1141.         end; (* if *)
  1142.         ICRGetMapEntry := err;
  1143.     end; (* ICRGetMapEntry *)
  1144.  
  1145.     function ICRSetMapEntry (var inst: ICRRecord; entries: handle; pos: longInt; var entry: ICMapEntry): ICError;
  1146.         var
  1147.             err: ICError;
  1148.             e: ICMapEntry;
  1149.             oldentry: ICMapEntry;
  1150.             user_length: longInt;
  1151.             source_length: longInt;
  1152.             junk: longInt;
  1153.     begin
  1154.         err := noErr;
  1155.         if (entries = nil) | (entries^ = nil) | (pos < 0) | (pos >= GetHandleSize(entries)) then begin
  1156.             err := paramErr;
  1157.         end; (* if *)
  1158.         if err = noErr then begin
  1159.             err := UnpackEntry(entries, pos, oldentry, user_length);
  1160.         end; (* if *)
  1161.         if err = noErr then begin
  1162.             PackEntry(entry, @e, user_length);
  1163.             source_length := oldentry.total_length - user_length;
  1164.             if user_length < 8 then begin { hack to remove alignment bytes from previous version }
  1165.                 source_length := oldentry.total_length;
  1166.                 e.total_length := e.total_length - user_length;
  1167.                 user_length := 0;
  1168.             end;
  1169.             junk := Munger(entries, pos, nil, source_length, @e, e.total_length - user_length);
  1170.             err := MemError;
  1171.         end;
  1172.         ICRSetMapEntry := err;
  1173.     end; (* ICRSetMapEntry *)
  1174.  
  1175.     function ICRDeleteMapEntry (var inst: ICRRecord; entries: handle; pos: longint): ICError;
  1176.         var
  1177.             err: ICError;
  1178.             entry: ICMapEntry;
  1179.             junk: longint;
  1180.             user_length: longInt;
  1181.     begin
  1182.         err := noErr;
  1183.         if (entries = nil) | (entries^ = nil) | (pos < 0) | (pos >= GetHandleSize(entries)) then begin
  1184.             err := paramErr;
  1185.         end; (* if *)
  1186.         if err = noErr then begin
  1187.             err := UnpackEntry(entries, pos, entry, user_length);
  1188.         end; (* if *)
  1189.         if err = noErr then begin
  1190.             junk := Munger(entries, pos, nil, entry.total_length, Ptr(-1), 0);
  1191.             err := MemError;
  1192.         end;
  1193.         ICRDeleteMapEntry := err;
  1194.     end; (* ICRDeleteMapEntry *)
  1195.  
  1196.     function ICRAddMapEntry (var inst: ICRRecord; entries: handle; var entry: ICMapEntry): ICError;
  1197.         var
  1198.             err: ICError;
  1199.             tmp_entry: ICMapEntry;
  1200.     begin
  1201.         err := noErr;
  1202.         if (entries = nil) | (entries^ = nil) then begin
  1203.             err := paramErr;
  1204.         end; (* if *)
  1205.         if err = noErr then begin
  1206.             PackEntry(entry, @tmp_entry, 0);
  1207.             err := PtrAndHand(@tmp_entry, entries, entry.total_length);
  1208.         end; (* if *)
  1209.         ICRAddMapEntry := err;
  1210.     end; (* ICRAddMapEntry *)
  1211.  
  1212. (* High Level Mapping Subs *)
  1213.  
  1214.     function ICRMapEntriesFilename (var inst: ICRRecord; entries: Handle; filename: Str255; var entry: ICMapEntry): ICError;
  1215.         (* implementation lifted directly from Space Aliens *)
  1216.         var
  1217.             err: ICError;
  1218.             longest_len: integer;
  1219.             posndx: longint;
  1220.             found_pos: longint;
  1221.     begin
  1222.         err := noErr;
  1223.         if (entries = nil) | (entries^ = nil) | (filename = '') then begin
  1224.             err := paramErr;
  1225.         end; (* if *)
  1226.         if err = noErr then begin
  1227.             (* loop through the entries *)
  1228.             (* looking for the longest match *)
  1229.             longest_len := 0;
  1230.             posndx := 0;
  1231.             while FastGetEntry(entries, posndx, entry) = noErr do begin
  1232.                 (* the entry matches if *)
  1233.                 (* not_incoming flag bit is clear *)
  1234.                 (* it's longer than the previous max *)
  1235.                 (* it's longer than the file name *)
  1236.                 (* it matches the last N chars of the filename *)
  1237.                 if (length(entry.extension) > longest_len) & not btst(entry.flags, ICmap_not_incoming_bit) & IsExtensionVar(filename, entry.extension) then begin
  1238.                     (* record the new longest entry *)
  1239.                     found_pos := posndx;
  1240.                     longest_len := length(entry.extension);
  1241.                 end; (* if *)
  1242.                 (* increment posndx so that we get the next *)
  1243.                 (* entry the next time around the loop *)
  1244.                 posndx := posndx + entry.total_length;
  1245.             end; (* while *)
  1246.         end; (* if *)
  1247.         if (err = noErr) & (longest_len = 0) then begin
  1248.             err := icPrefNotFoundErr;
  1249.         end; (* if *)
  1250.         if (err = noErr) then begin
  1251.             err := ICRGetMapEntry(inst, entries, found_pos, entry);
  1252.         end; (* if *)
  1253.         ICRMapEntriesFilename := err;
  1254.     end; (* ICRMapEntriesFilename *)
  1255.  
  1256.     function ICRMapEntriesTypeCreator (var inst: ICRRecord; entries: Handle; fType, fCreator: OSType; filename: Str255; var entry: ICMapEntry): ICError;
  1257.         var
  1258.             err: ICError;
  1259.             posndx: longint;
  1260.             found_pos: longint;
  1261.             match_weight: longint;
  1262.             best_weight: longint;
  1263.     begin
  1264.         err := noErr;
  1265.         if (entries = nil) | (entries^ = nil) then begin
  1266.             err := paramErr;
  1267.         end; (* if *)
  1268.         if err = noErr then begin
  1269.             posndx := 0;
  1270.             best_weight := -1;
  1271.             while FastGetEntry(entries, posndx, entry) = noErr do begin
  1272.                 if not btst(entry.flags, ICmap_not_outgoing_bit) then begin
  1273.                     if entry.file_type = fType then begin
  1274.                         match_weight := ord(entry.file_creator = fCreator);
  1275.                         if IsExtensionVar(filename, entry.extension) then begin
  1276.                             match_weight := match_weight + 2 * length(entry.extension);
  1277.                         end; (* if *)
  1278.                         if match_weight > best_weight then begin
  1279.                             (* record the new longest entry *)
  1280.                             found_pos := posndx;
  1281.                             best_weight := match_weight;
  1282.                         end; (* if *)
  1283.                     end; (* if *)
  1284.                 end; (* if *)
  1285.                 posndx := posndx + entry.total_length;
  1286.             end; (* while *)
  1287.             if best_weight = -1 then begin
  1288.                 err := icPrefNotFoundErr;
  1289.             end
  1290.             else begin
  1291.                 err := ICRGetMapEntry(inst, entries, found_pos, entry);
  1292.             end; (* if *)
  1293.         end; (* if *)
  1294.         ICRMapEntriesTypeCreator := err;
  1295.     end; (* ICRMapEntriesTypeCreator *)
  1296.  
  1297. (* High Level Mapping Routines *)
  1298.  
  1299.     function ICRMapFilename (var inst: ICRRecord; filename: Str255; var entry: ICMapEntry): ICError;
  1300.         var
  1301.             err: ICError;
  1302.             entries: Handle;
  1303.             junk_attr: ICAttr;
  1304.     begin
  1305.         err := noErr;
  1306.         if filename = '' then begin
  1307.             err := paramErr;
  1308.         end; (* if *)
  1309.         if err = noErr then begin
  1310.             err := ICRGetPrefHandle(inst, kICMapping, junk_attr, entries);
  1311.         end; (* if *)
  1312.         if err = noErr then begin
  1313.             err := ICRMapEntriesFilename(inst, entries, filename, entry);
  1314.             DisposeHandle(entries);
  1315.         end; (* if *)
  1316.         ICRMapFilename := err;
  1317.     end; (* ICRMapFilename *)
  1318.  
  1319.     function ICRMapTypeCreator (var inst: ICRRecord; fType, fCreator: OSType; filename: Str255; var entry: ICMapEntry): ICError;
  1320.         var
  1321.             err: ICError;
  1322.             entries: Handle;
  1323.             junk_attr: ICAttr;
  1324.     begin
  1325.         err := ICRGetPrefHandle(inst, kICMapping, junk_attr, entries);
  1326.         if err = noErr then begin
  1327.             err := ICRMapEntriesTypeCreator(inst, entries, fType, fCreator, filename, entry);
  1328.             DisposeHandle(entries);
  1329.         end; (* if *)
  1330.         ICRMapTypeCreator := err;
  1331.     end; (* ICRMapTypeCreator *)
  1332.  
  1333. end. (* ICRAPI *)